/*
 * @(#)GenericPeer.java Dec 25, 2011 - 12:32:56 PM
 * 
 * Copyright 2011 Cin/UFPE/VSPEED
 */
package br.ufpe.cin.vspeed.integration;

import java.lang.reflect.Field;
import java.util.Hashtable;
import java.util.Vector;

import speed2010.application.gui.VirtualNodeInfo;
import speed2010.mensagem.Mensagem;
import speed2010.mensagem.MsgTipo;
import speed2010.mensagem.TipoDaFila;
import speed2010.middleware.comunicacao.Address;
import speed2010.middleware.serviceOverlay.node.Node;
import speed2010.middleware.serviceOverlay.node.NodeType;
import speed2010.reformulacao.query.Query;

/**
 * @author <a href="mailto:andreza.lba@gmail.com">Andreza Leite de Alencar</a>
 */
public class NodeGeneric extends Node {
	private final InvokerGeneric invokerG;
	private Address psAddress;
	private Address piAddress;

	private final Hashtable<String, VirtualNodeInfo> mapAddressReduzido_NodeInfo;
	private final Hashtable<Integer, Address> local_mapID_Address;

	public NodeGeneric(final Address address, final NodeType nodeType, final String novoDominio) throws Exception {
		super(address, nodeType, novoDominio);
		this.invokerG = new InvokerGeneric(this);
		this.mapAddressReduzido_NodeInfo = new Hashtable<String, VirtualNodeInfo>();
		this.local_mapID_Address = new Hashtable<Integer, Address>();
		setInvoker();
	}

	/**
	 * @return Returns the psAddress.
	 */
	public Address getPsAddress() {
		return this.psAddress;
	}

	/**
	 * Changes the value of psAddress as the parameter.
	 * 
	 * @param psAddress
	 */
	public void setPsAddress(final Address addressPS) {
		this.psAddress = addressPS;
	}

	/**
	 * @return Returns the piAddress.
	 */
	public Address getPiAddress() {
		return this.piAddress;
	}

	/**
	 * Changes the value of piAddress as the parameter.
	 * 
	 * @param piAddress
	 */
	public void setPiAddress(final Address addressPI) {
		this.piAddress = addressPI;
	}

	/**
	 * @return Returns the mapAddressReduzido_NodeInfo.
	 */
	public Hashtable<String, VirtualNodeInfo> getMapAddressReduzido_NodeInfo() {
		return this.mapAddressReduzido_NodeInfo;
	}

	/**
	 * @return Returns the local_mapID_Address.
	 */
	@Override
	public Hashtable<Integer, Address> getLocal_mapID_Address() {
		return this.local_mapID_Address;
	}

	/**
	 * Utilizado para trocar a instancia o invoker do superclasse pela implementação local
	 * 
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	private void setInvoker() throws NoSuchFieldException, IllegalAccessException {
		final Class<?> clazz = this.getClass().getSuperclass();
		final Field declaredField = clazz.getDeclaredField("invokerP");
		declaredField.setAccessible(true);
		declaredField.set(this, this.invokerG);
	}

	/**
	 * @return Returns the invokerG.
	 */
	public InvokerGeneric getInvokerG() {
		return this.invokerG;
	}

	public void processaMsgs(final Mensagem msg) {
		switch (msg.getTipoDaFila()) {
			case ENTRADA :
				this.invokerG.processaMsgDeEntrada(msg);
				break;
			case SAIDA :
				super.getInvokerP().processaMsgDeSaida(msg);
				break;
		}
	}

	public void sendMsg(final String ipAddress, final int port, final MsgTipo tipoMsg) {
		final Address destination = new Address(ipAddress, port);
		this.sendMsg(destination, tipoMsg);
	}

	public void sendMsg(final Address destination, final MsgTipo tipoMsg) {
		this.sendMsg(destination, tipoMsg, -1);
	}

	public void sendMsg(final Address destination, final MsgTipo tipoMsg, final long msgId) {
		final Vector<String> dadosDeControle = new Vector<String>();
		final String creationTimeStamp = String.valueOf(System.currentTimeMillis());
		dadosDeControle.add(creationTimeStamp);
		final Address origem = getAddress();
		dadosDeControle.add(origem.toStringReduzido());

		final TipoDaFila fila = TipoDaFila.SAIDA;

		final Mensagem msg = new Mensagem(fila, origem, destination, msgId, tipoMsg, dadosDeControle, null);
		getMsgQueue().adicionaMsg(msg);
	}

	public void sendQueryMsg(final Address destination, final Query query) {
		final Vector<String> dadosDeControle = new Vector<String>();
		final String creationTimeStamp = String.valueOf(System.currentTimeMillis());
		dadosDeControle.add(creationTimeStamp);
		final Address origem = getAddress();
		dadosDeControle.add(origem.toStringReduzido());

		final Vector<Object> dados = new Vector<Object>();
		dados.add(query);

		final TipoDaFila fila = TipoDaFila.SAIDA;

		final Mensagem msg = new Mensagem(fila, origem, destination, -1, MsgTipo.PI_QUERY, dadosDeControle, dados);
		getMsgQueue().adicionaMsg(msg);
	}
}
